#include <iostream>
#include <stdlib.h>
#include <errno.h>
#include "Conexion.h"
#include "ProtocoloConstantes.h"

#define BUF_SIZE 500
#define CONEXION_TIMEOUT 20

using namespace TP::Comunicacion;

Conexion::Conexion(const string& pIp, const string& pPuerto) {
	this->ip = pIp;
	this->puerto = pPuerto;
	this->ConexionPerdida = false;
}

Conexion::~Conexion() { }

void Conexion::Iniciar() throw (Excepcion&) {
	try {
		if (-1 == (this->sockfd = socket(AF_INET, SOCK_STREAM, 0))) {
			string msg = "No se puede iniciar el socket. ";
			msg += strerror(errno);
			throw Excepcion(msg);
		}

		struct sockaddr_in dest_addr;

		dest_addr.sin_family = AF_INET;
		dest_addr.sin_port = htons(atoi(this->puerto.c_str()));
		dest_addr.sin_addr.s_addr = inet_addr(this->ip.c_str());
		memset(&(dest_addr.sin_zero), '\0', 8);

		if (connect(this->sockfd, (struct sockaddr*)&dest_addr,
				sizeof (struct sockaddr)) == -1) {
			throw Excepcion("No se puede conectar al host "	+ this->ip
					+ " puerto " + this->puerto	+ ". " + strerror(errno));
		}
	}
	catch (Excepcion& pEx) {
		if (-1 != this->sockfd)
			shutdown(this->sockfd, SHUT_RDWR);

		pEx.expandirMensaje("No se puede iniciar la conexion. ");
		throw;
	}
}

void Conexion::Finalizar() throw (Excepcion&) {
	try {
		// Se finaliza la conexion.
		close(this->sockfd);
	}
	catch (Excepcion& pEx) {
		pEx.expandirMensaje("No se puede finalizar la conexion.");
		throw;
	}
}

int Conexion::Escribir(const string& pMensaje) throw (Excepcion&) {
	int cntBytes = -1;

	if (this->ConexionPerdida)
		throw Excepcion("Conexion perdida.");

	try {
		/* Toma el mensaje, y lo escribe a traves de la conexion. */
		string msg_env = pMensaje;

		// Finalizo el mensaje para transmision.
		msg_env += FIN_MENSAJE;

		// Envio el mensaje.
		cntBytes = send(this->sockfd, msg_env.c_str(), msg_env.size(), 0);

		if (cntBytes <= 0)
			throw Excepcion("Fallo de comunicacion con el servidor.");
	}
	catch (Excepcion& pEx) {
		pEx.expandirMensaje("No se puede escribir el mensaje.");
		throw;
	}
	return cntBytes;
}

int Conexion::Leer(string& pMensaje) throw (Excepcion&) {
	try {
		/* Llegado el caso, aca es donde se tiene que encodear el mensaje para
		 * el envio	 */
		string mensaje = "";
		char buffer [BUF_SIZE];
		bzero(buffer, BUF_SIZE);
		int bytes_recv = -1;
		int intentos = 10;

		while (true) {
			// Inicializo el buffer.
			bzero(buffer, BUF_SIZE);
			bytes_recv = recv(this->sockfd, buffer, 255, 0);

			if (bytes_recv <= 0) {
				intentos--;
				if (intentos == 0)
					throw CONEXION_TIMEOUT;
				else
					sleep(1);
			}
			else {
				// Vuelco el buffer en el mensaje.
				for (int i = 0; i < bytes_recv; i++) {
					if (FIN_MENSAJE != buffer[i])
						mensaje += buffer[i];
					else {
						// Decodifico el mensaje 
						for (unsigned int i = 0; i < mensaje.size(); i++)
							if (SALTO_LINEA == mensaje[i])
								mensaje [i] = '\n';

						// Notifico a los manejadores de mensaje entrante.
						pMensaje = mensaje;
						return pMensaje.size();
					}
				}
			}
		}
	}
	catch (int pEx) {
		if (CONEXION_TIMEOUT)
			throw Excepcion("Conexion Timeout");
	}
	catch (Excepcion& pEx) {
		pEx.expandirMensaje("No se puede leer el mensaje desde el servidor.");
		throw;
	}
}